﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net.Sockets;
using System.Net;
using System.Threading;

namespace ServerFrame
{
    public class Server
    {
        int maxconn;               //最大连接数
        int port;                  //端口
        int waitconn;              //可挂起的连接数
        Semaphore maxconn_ex;      //规定信号量最大值
        Socket listenSocket;       //监听
        Objpool<Mytoken> tokenpool;//读写池（存放Mytoken类型）
        
        public AbsMessageCenter center;//创建处理中心
        /// <summary>
        /// 
        /// </summary>
        /// <param name="port">端口数</param>
        /// <param name="maxconn">最大连接数</param>
        /// <param name="waitconn">可挂起的连接数（默认10）</param>
        public Server(int port, int maxconn,int waitconn=10)
        {
            this.port = port;
            this.maxconn = maxconn;
            this.waitconn = waitconn;
            maxconn_ex = new Semaphore(maxconn, maxconn);//从maxconn开始减 到零停止
        }
        /// <summary>
        /// 初始化
        /// </summary>
        public void init()
        {
            tokenpool = new Objpool<Mytoken>(maxconn); //创建读写池
            for (int i = 0; i < maxconn; i++)
            {
                Mytoken token = new Mytoken();
                //为读写异步操作绑定完成事件
                token.args_read.Completed += new EventHandler<SocketAsyncEventArgs>(IO_Completed);
                token.args_write.Completed += new EventHandler<SocketAsyncEventArgs>(IO_Completed);
                token.center = center;
                token.send_de += ProcessSend;
                token.close_de += CloseClientSocket;
                tokenpool.Push(token); //将新建的token存入读写池中的栈列
            }
        }
        /// <summary>
        /// 绑定IP地址
        /// </summary>
        /// <param name="localEndPoint"></param>
        public void start()
        {
            init();
            try
            {
            listenSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); //初始化套接字，采用TCP协议，地址是IPv4地址
            listenSocket.Bind(new IPEndPoint(IPAddress.Any,port)); //关联所有IP地址（IPv4）
            listenSocket.Listen(waitconn); //监听最大可挂起的连接数
            Console.WriteLine("start listen");
            StartAccept(null);
            }
            catch(Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }

        }
        /// <summary>
        /// 开始异步监听
        /// </summary>
        /// <param name="acceptEvenArgs">异步处理事件</param>
        public void StartAccept(SocketAsyncEventArgs acceptEventArg)
        {
            //判断当前异步事件是否为空
            if (acceptEventArg == null) //如果当前异步事件为空，为事件e绑定一个完成事件
            {
                acceptEventArg = new SocketAsyncEventArgs();
                acceptEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(acceptEventArg_Completed);
            }
            else //如果当前异步事件不为空，则证明e已有连接，则清空连接，为下一个连接留位置
            {
                acceptEventArg.AcceptSocket = null;
            }
            maxconn_ex.WaitOne(); //信号量减一
            try
            {
                bool result = listenSocket.AcceptAsync(acceptEventArg);
                if (!result)
                {
                    ProcessAccept(acceptEventArg);
                } //如果被挂起，则等待连接，执行Completed操作
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }
        }
        /// <summary>
        /// 异步完成事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e">异步处理事件</param>
        public void acceptEventArg_Completed(object sender, SocketAsyncEventArgs e)
        {
            ProcessAccept(e);
        }
        /// <summary>
        /// 处理监听到的连接
        /// </summary>
        /// <param name="e"></param>
        public void ProcessAccept(SocketAsyncEventArgs e)
        {
            //从对象池中取出读写 异步操作
            Mytoken token = tokenpool.Pop(); //取出当前读写池中栈列的Mytoken
            token.socket = e.AcceptSocket; //将当前异步操作中的socket赋给token中的socket
            //通知上层有客户端访问
            center.OnClientConnected(token);
            //测试
            //token.send(new TransModel(1, 1, 1, "hello"));
            try
            {
                //开始异步操作
                bool result_read = token.socket.ReceiveAsync(token.args_read); //判断异步写入操作是否为空,获取数据
                if (!result_read)
                {
                    ProcessReceive(token.args_read);
                }
                StartAccept(e);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
                CloseClientSocket(token, "客户端断开连接");
            }
            
        }
        public void IO_Completed(object sender, SocketAsyncEventArgs e)
        {
                switch (e.LastOperation)//检测异步操作的最后一步
            {
                case SocketAsyncOperation.Receive://如果异步操作的最后一步是写入,则执行ProcessReceive（）方法
                    ProcessReceive(e);
                    break;
                case SocketAsyncOperation.Send://如果异步操作的最后一步是读取,则执行ProcessSend（）方法
                    ProcessSend(e);
                    break;
                default:
                    throw new ArgumentException("The last operation completed on the socket was not a receive or send");
            }       
        }
        /// <summary>
        /// 异步写入操作
        /// </summary>
        /// <param name="e"></param>
        public void ProcessReceive(SocketAsyncEventArgs e)//e->token.args_read
        {
            Mytoken token = (Mytoken)e.UserToken;
            //判断连接是否还存在
            if (e.BytesTransferred > 0 && e.SocketError == SocketError.Success)
            {
                //接受的数据存放在e.BytesTransferred中
                //调用token中的接收方法，开始处理数据
                byte[] data = new byte[e.BytesTransferred];
                Buffer.BlockCopy(e.Buffer, 0, data, 0, e.BytesTransferred);
                token.Receive(data);
                try
                {
                    //循环接受
                    bool result = token.socket.ReceiveAsync(token.args_read);//判断是否挂起
                    if (!result)
                    {
                        ProcessReceive(e);
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.ToString());
                    CloseClientSocket(token, "客户端断开连接");
                }
            }
            else
            {
                CloseClientSocket(token,"客户端断开连接");
            }
        }
        /// <summary>
        /// 异步读取操作
        /// </summary>
        /// <param name="e"></param>
        public void ProcessSend(SocketAsyncEventArgs e)//e->token.args_write
        {
            Mytoken token = (Mytoken)e.UserToken;
            //读取socket中接受到的字节数，如果大于零且socket状态为success，就写入异步操作中的数据
            if (e.BytesTransferred > 0 && e.SocketError == SocketError.Success)
            {
                token.write();
            }
            else
            {
                CloseClientSocket(token, "客户端断开连接");
            }
        }
        /// <summary>
        /// 通知上层，客户端断开连接，清空token数据，放入对象池（下一个客户代替）
        /// </summary>
        /// <param name="e"></param>
        /// <param name="error">错误原因</param>
        public void CloseClientSocket(Mytoken token, string error)
        {
            //关闭token时，外部不应继续调用，所以上锁
            lock (token)
            { 
                center.OnClientClose(token , error);
                token.Close();
                tokenpool.Push(token); //将废弃的token压入栈中，让下一个客户使用
                maxconn_ex.Release(); //信号量加一
            }
        }
    }
}
